package maple.core.applications;

import java.util.Date;
import java.util.HashMap;
import java.util.Vector;

import jade.lang.acl.ACLMessage;

import maple.core.Application;
import maple.lang.Command;
import maple.lang.Language;
import maple.util.Actuator;
import maple.util.Context;
import maple.util.Planner;
import maple.util.Profile;
import maple.util.Task;
import maple.util.tasks.ApplicationTask;
import maple.util.tasks.MasterTask;
import maple.util.Service;
import maple.util.Timestamp;
import maple.util.Tuple;

public class AutoLighting extends Application
{
	private final long DELAY_ON = 25000;
	private final long DELAY_OFF = 10000;
	private final long WAIT_TIME = 600000;
	
	private final String HP_COMMAND = Language.Instruction.SELECT.toString()
									  + Language.DELIMITER
									  + Service.Property.NAME.toString()
									  + Language.ASSIGN
									  + Language.QUOTE
									  + Language.Instruction.INTERPRET.toString()
									  + Language.QUOTE
									  + Language.DELIMITER
									  + Service.Property.TYPE.toString()
									  + Language.ASSIGN
									  + Language.QUOTE
									  + Context.Type.HUMANPRESENCE.toString()
									  + Language.QUOTE;

	private String _hpagent_name;
	
	private boolean _off;
	private long _touched_time;

	public AutoLighting()
	{
		super( 600, 0 );

		// from Consumer 
		_preconditions.add( Context.Type.LAMPSTATE.toString() ); 
		_preconditions.add( Context.Type.MOTIONLEVEL.toString() ); 
		_preconditions.add( Context.Type.ILLUMINATIONLEVEL.toString() ); 
		_postcondition = AutoLighting.class.getSimpleName();

		// from Application
		_tuples = new Tuple[ ( _preconditions.size() + 1 ) * _size ];
		
		_hpagent_name = null;
		
		_off = false;
		_touched_time = 0;
	}

	protected void setup()
	{
		super.setup();

		String master = findMaster();
		if( master != null )
		{
			sendMessage( ACLMessage.REQUEST, master, HP_COMMAND, null, null );
		}
		else
		{
			doDelete();
		}
	}

	protected void actionInform( final ACLMessage message )
	{
		_logger.info( message.getSender().getLocalName() + Language.DELIMITER + message.getContent() );
		//System.out.println( Timestamp.Default.format( new Date( System.currentTimeMillis() ) ) + Language.DELIMITER + message.getSender().getLocalName() + Language.DELIMITER + message.getContent() );

		String command = message.getUserDefinedParameter( Task.Property.COMMAND.toString() );
		if( command == null )
		{
			String content = message.getContent();
			if( !content.equals( Command.Message.NOT_FOUND.toString() ) )
			{
				String[] tokens = content.split( Language.DELIMITER );
				_hpagent_name = tokens[ 0 ];
			}
		}
		else
		{
			super.actionInform( message );
		}
	}

	private void presence( final String location )
	{
		String motion_level = "";
		for( Tuple tuple : _tuples )
		{
			if( tuple != null && tuple.isValid() )
			{
				if( tuple.getType().equals( location + Language.DELIMITER + _preconditions.get( 1 ) ) )
				{
					motion_level = motion_level + Language.COMMA + tuple.getValue();
				}
			}
		}

		if( motion_level.length() > 0 )
		{
			motion_level = motion_level.substring( 1 );
			
			String command = Language.Instruction.INTERPRET.toString()
							 + Language.DELIMITER
							 + _preconditions.get( 1 )
							 + Language.ASSIGN
							 + Language.QUOTE
							 + motion_level
							 + Language.QUOTE;

			HashMap< String, String > parameters = new HashMap< String, String >();
			parameters.put( Task.Property.COMMAND.toString(), command );
			parameters.put( Planner.Property.POSTCONDITION.toString(), _postcondition );
			parameters.put( Profile.Property.LOCATION.toString(), location );
			
			sendMessage( ACLMessage.REQUEST, _hpagent_name, command, parameters, null );
		}
	}

	/**
	 * execute the specified task
	 *
	 * @param	task	the specified task
	 *
	 * @author	snowfoxy
	 * @since	2011-11-27
	 */
	protected void subscribe( final ApplicationTask task )
	{
		long current = System.currentTimeMillis();

		Vector< String > locations = task.getLocations();
		Vector< Tuple > states = new Vector< Tuple >();
		for( int i = 0; i < locations.size(); ++i )
		{
			Tuple tuple = _perceptions.get( locations.get( i ) + Language.DELIMITER + _preconditions.get( i ) );
			if( tuple != null && tuple.isValid() && ( tuple.getTimestamp() - current ) < 5 * INTERVAL )
			{
				states.add( tuple );
			}
		}

		if( states.size() == locations.size() && _hpagent_name != null )
		{
			String lamp_state = states.get( 0 ).getValue();
			String illumination_level = states.get( 2 ).getValue();

			presence( locations.get( 1 ) );
			Tuple tuple = _perceptions.get( locations.get( 1 ) + Language.DELIMITER + Context.Type.HUMANPRESENCE.toString() );
			if( tuple != null )
			{
				if( states.get( 0 ).getTimestamp() > _touched_time )
				{
					String presence = tuple.getValue();
					if( presence.equals( Context.HumanPresence.TRUE.toString() ) )
					{
						if( illumination_level.equals( Context.IlluminationLevel.LOW.toString() ) 
							&& lamp_state.equals( Context.LampState.OFF.toString() ) )
						{
							touch( task.getMaster(), locations.get( 0 ) );
							System.out.println( Timestamp.Default.format( new Date( current ) ) + Language.DELIMITER + "TURN ON!" );
							_logger.info( "TURN ON!" );
						
							_touched_time = current + DELAY_ON;
						}
						_off = false;
					}
					else if( presence.equals( Context.HumanPresence.FALSE.toString() ) )
					{
						if( lamp_state.equals( Context.LampState.ON.toString() ) )
						{
							touch( task.getMaster(), locations.get( 0 ) );
							if( _off )
							{
								System.out.println( Timestamp.Default.format( new Date( current ) ) + Language.DELIMITER + "TRY AGAIN!" );
								_logger.info( "TRY AGAIN!" );
								
								_off = false;
								_touched_time = current + WAIT_TIME;
							}
							else
							{
								System.out.println( Timestamp.Default.format( new Date( current ) ) + Language.DELIMITER + "TURN OFF!" );
								_logger.info( "TURN OFF!" );
								
								_off = true;
								_touched_time = current + DELAY_OFF;
							}
						}
					}
					else
					{
						_off = false;
					}
				}
			}
		}
	}

	/**
	 * touch the switch
	 *	
	 * @param	master		the specified master
	 * @param	location	the specified location
	 *
	 * @author	snowfoxy
	 * @since	2011-11-27
	 */
	private void touch( final String master, final String location )
	{
		String command = Language.Instruction.SET.toString()
						 + Language.DELIMITER
						 + MasterTask.Property.GOAL.toString()
						 + Language.ASSIGN
						 + Language.QUOTE
						 + Actuator.Type.LAMP.toString()
						 + Language.QUOTE
						 + Language.DELIMITER
						 + MasterTask.Property.INTERVAL.toString()
						 + Language.ASSIGN
						 + Language.QUOTE
						 + "-1"
						 + Language.QUOTE
						 + Language.DELIMITER
						 + MasterTask.Property.LOCATION.toString()
						 + Language.ASSIGN
						 + Language.QUOTE
						 + location
						 + Language.QUOTE;
		sendMessage( ACLMessage.REQUEST, master, command, null, null );
	}
}
